package com.sojson.util;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.annotation.PostConstruct;

import org.springframework.aop.framework.AopContext;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;

import lombok.extern.slf4j.Slf4j;

/**
 * 用来获取Spring中的Bean对象
 * 
 * @author liu
 * @date 2020-11-02
 */
@Slf4j
@Configuration
public class SpringUtil {

    @Autowired
    private ApplicationContext ac;
    /** 核心类 */
    private static ApplicationContext applicationContext;
    /** Bean工厂类 */
    private static DefaultListableBeanFactory defaultListableBeanFactory;

    /**
     * 初始化
     */
    @PostConstruct
    public void init() {
        // 设置ApplicationContext
        setApplicationContext(ac);
        ac = null;

        // 设置DefaultListableBeanFactory
        // 将ApplicationContext转换为ConfigurableApplicationContext
        ConfigurableApplicationContext configurableApplicationContext =
            (ConfigurableApplicationContext)getApplicationContext();
        // 获取Bean工厂并转换为DefaultListableBeanFactory
        DefaultListableBeanFactory defaultListableBeanFactory =
            (DefaultListableBeanFactory)configurableApplicationContext.getBeanFactory();
        setDefaultListableBeanFactory(defaultListableBeanFactory);
    }

    /** 获取 ApplicationContext */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /** 设置 ApplicationContext */
    public static void setApplicationContext(ApplicationContext applicationContext) {
        SpringUtil.applicationContext = applicationContext;
        log.info("ApplicationContext注入成功");
    }

    /** 获取 DefaultListableBeanFactory */
    public static DefaultListableBeanFactory getDefaultListableBeanFactory() {
        return defaultListableBeanFactory;
    }

    /** 设置 DefaultListableBeanFactory */
    public static void setDefaultListableBeanFactory(DefaultListableBeanFactory defaultListableBeanFactory) {
        SpringUtil.defaultListableBeanFactory = defaultListableBeanFactory;
        log.info("DefaultListableBeanFactory注入成功");
    }

    /**
     * 通过bean名字获取一个bean对象
     * 
     * @param name bean的名字
     * @return
     */
    public static Object getBeanByName(String name) {
        return applicationContext.getBean(name);
    }

    /**
     * 通过bean的类型获取一个bean对象
     * 
     * @param requiredType bean的类型
     * @return
     */
    public static <T> T getBeanByType(Class<T> requiredType) {
        return applicationContext.getBean(requiredType);
    }

    /**
     * 通过bean名子和bean的类型获取一个bean对象
     * 
     * @param name bean的名字
     * @param requiredType bean的类型
     * @return
     */
    public static <T> T getBean(String name, Class<T> requiredType) {
        return applicationContext.getBean(requiredType);
    }

    /**
     * 判断是否有这个bean对象
     * 
     * @param name bean的名字
     * @return
     */
    public static boolean containsBean(String name) {
        return applicationContext.containsBean(name);
    }

    /**
     * 判断一个bean对象是否是单例类
     * 
     * @param name bean的名字
     * @return
     */
    public static boolean isSingleton(String name) {
        return applicationContext.isSingleton(name);
    }

    /**
     * 通过一个bean的名字获取他的类型
     * 
     * @param name bean的名字
     * @return
     */
    public static Class<? extends Object> getType(String name) {
        return applicationContext.getType(name);
    }

    /**
     * 返回一个bean对象的所有别名数组，不包含参数中的名字
     * 
     * @param name bean的名字
     * @return
     */
    public static String[] getAliases(String name) {
        return applicationContext.getAliases(name);
    }

    /**
     * 注册一个Bean对象
     * 
     * @param name  bean的名字
     * @param obj   bean对象
     * @return
     */
    public static void add(Object obj) {
        defaultListableBeanFactory.registerSingleton(getBeanName(obj.getClass()), obj);
    }

    /**
     * 注册一个Bean对象
     * 
     * @param name  bean的名字
     * @param obj   bean对象
     * @return
     */
    public static void add(String name, Object obj) {
        defaultListableBeanFactory.registerSingleton(name, obj);
    }

    /**
     * 注册Bean
     * 
     * @param registry
     * @param beanClass
     */
    public static void registerBean(BeanDefinitionRegistry registry, Class<?> beanClass) {
        registerBean(registry, beanClass, getBeanName(beanClass));
    }

    /**
     * 注册Bean
     * 
     * @param registry
     * @param beanClass
     * @param name
     */
    public static void registerBean(BeanDefinitionRegistry registry, Class<?> beanClass, String name) {
        registerBean(registry, beanClass, name, null, null);
    }

    /**
     * 注册Bean
     * 
     * @param registry
     * @param beanClass Bean类型
     * @param name      Bean名称
     * @param conParams 构造参数列表
     */
    public static void registerBean(BeanDefinitionRegistry registry, Class<?> beanClass, String name,
        List<Object> conParams) {
        registerBean(registry, beanClass, name, conParams, null);
    }

    /**
     * 注册Bean
     * 
     * @param registry
     * @param beanClass Bean类型
     * @param name      Bean名称
     * @param params    参数集合
     */
    public static void registerBean(BeanDefinitionRegistry registry, Class<?> beanClass, String name,
        Map<String, Object> params) {
        registerBean(registry, beanClass, name, null, params);
    }

    /**
     * 注册Bean
     * 
     * @param registry
     * @param beanClass Bean类型
     * @param name      Bean名称
     * @param conParams 构造参数列表
     * @param params    参数集合
     */
    public static void registerBean(BeanDefinitionRegistry registry, Class<?> beanClass, String name,
        List<Object> conParams, Map<String, Object> params) {
        // 选择构造方法并给构造方法传参
        ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
        if (conParams != null) {
            for (int i = 0; i < conParams.size(); i++) {
                constructorArgumentValues.addIndexedArgumentValue(i, conParams.get(i));
            }
        }

        // 给参数赋值
        MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
        if (params != null) {
            Set<Entry<String, Object>> entrySet = params.entrySet();
            for (Entry<String, Object> entry : entrySet) {
                mutablePropertyValues.add(entry.getKey(), entry.getValue());
            }
        }

        // 注入
        RootBeanDefinition bean = new RootBeanDefinition(beanClass, constructorArgumentValues, mutablePropertyValues);
        registry.registerBeanDefinition(name, bean);
    }

    /**
     * 获取Bean名称
     * 
     * @param beanClass
     * @return
     */
    private static String getBeanName(Class<?> beanClass) {
        String name = beanClass.getName();
        name = name.substring(name.lastIndexOf(".") + 1);
        name = name.substring(0, 1).toLowerCase() + (name.length() > 1 ? name.substring(1) : "");
        return name;
    }

    /**
     * 获取Aop代理类
     * 
     * @param invoker
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getProxy(T invoker) {
        return (T)AopContext.currentProxy();
    }

}